import { Groove, Hole, Profile } from '@/types';
import {
  getPointQuadrant,
  groupPointsByQuadrant,
  point2point3,
  point3point2,
  xyz,
} from '@/utils';
import { concat, flow, isUndefined, negate, round } from 'lodash-es';
import { ComponentProps } from 'react';
import { Line3, Vector3 } from 'three';
import { Mark } from '../../Mark';
import {
  getBoundBoxVertexPointsFromPlank,
  getLinesFromPoints,
  getLinesFromReact,
} from '../utils';

export function PlankMark(props: {
  width: number;
  height: number;
  profile: Profile;
  holes: Hole[];
  grooves: Groove[];
}) {
  type MarkProps = ComponentProps<typeof Mark>;
  type Tick = NonNullable<MarkProps['ticks']>[0];
  type StartPoint = Pick<Hole, 'start'>;

  const { width, height, profile, holes, grooves } = props;

  const points = concat<StartPoint>(holes, grooves)
    .filter((hole) => hole.start)
    .map((hole) => point3point2(xyz(hole.start)));
  const groupedByQuadrantPoints = groupPointsByQuadrant(points);

  const lines = flow(
    getBoundBoxVertexPointsFromPlank,
    getLinesFromPoints,
  )({ width, height, profile });

  return (
    <>
      {lines
        .map((line) => line.map(point2point3))
        .map(([start, end]) => {
          const markQuadrants = [start, end].map((point) => {
            return getPointQuadrant(point3point2(point));
          });

          const markPoints = markQuadrants
            .map((markQuadrant) => {
              return groupedByQuadrantPoints[markQuadrant];
            })
            .filter(negate(isUndefined))
            .flat();

          const point3s = markPoints.map(point2point3);

          const [startVector3, endVector3] = [start, end].map((p) => {
            return new Vector3(...p);
          });
          const line3 = new Line3(startVector3, endVector3);
          const lineLen = line3.distance();
          const lineLenHalf = lineLen / 2;

          const lineMiddlePointVector3 = line3.getCenter(new Vector3());
          point3s.push(lineMiddlePointVector3.toArray());

          const ticks = point3s.map<Tick>((point3) => {
            const holeStartVector3 = new Vector3(...point3);
            const pointInlineVector3 = new Vector3();
            line3.closestPointToPoint(
              holeStartVector3,
              true,
              pointInlineVector3,
            );

            const position = pointInlineVector3.toArray();

            const distanceToStart = pointInlineVector3.distanceTo(startVector3);

            let edgeDistance =
              distanceToStart < lineLenHalf
                ? distanceToStart
                : lineLen - distanceToStart;

            let direction: Tick['direction'];
            if (distanceToStart < lineLenHalf) {
              direction = 'start';
            } else if (distanceToStart > lineLenHalf) {
              direction = 'end';
            }

            const text = round(edgeDistance).toFixed();
            return { position, text, direction };
          });

          return (
            <Mark
              key={`${start.toString()}-${end.toString()}`}
              {...{ start, end, ticks }}
            />
          );
        })}
    </>
  );
}
